package de.lmu.ifi.pst.uwejsf.framework;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

/**
 * Base class for the generated class <code>UWEContextFactory</code>. It provides
 * a default script context for OGNL that is used in the navigation model.
 * This context contains:
 * <ul>
 * 	<li>the managed beans for navigation nodes (see collection MANAGED_BEAN_NAMES in 
 * generated class UWEContextFactory)</li>
 * 	<li>if a visit object in the user model is called "Session" it is present 
 * as "uweSession" and "session" in OGNL</li>
 * 	<li>an instance of the class {@link UWEScriptHelper} as "uweHelper"</li>
 * 	<li>the application's persistence manager as "uwePersistenceManager"</li>
 * </ul>
 * 
 * 
 * @author Christian Kroiss
 *
 */
public abstract class AbstractUWEContextFactory implements
		ExpressionContextProvider {
	

	private ScriptEngineManager manager = new ScriptEngineManager();

	private Map<String, ScriptEngine> scriptEngines = new HashMap<String, ScriptEngine>();

	/**
	 * Creates a new script engine that is identified by the given name
	 * or returns a previously created instance that has been stored in 
	 * a map.
	 */
	@Override
	public ScriptEngine getScriptEngineByName(String name) {
		if (scriptEngines.get(name) == null) {
			ScriptEngine scriptEngine = manager.getEngineByName(name);
			scriptEngines.put(name, scriptEngine);
		}
		return scriptEngines.get(name);
	}

	/**
	 * Returns a list of the names of all managed beans that are
	 * created by the transformation. This method is implemented
	 * by the generated class <code>UWEContextFactory</code>.
	 * 
	 */
	public abstract List<String> getManagedBeanNames();


	@Override
	public abstract String getTypeMapping(String applicationTypeName);

	@Override
	public abstract String getReverseTypeMapping(String contentModelName);

	/**
	 * Returns the instance of the generated UWEContextFactory class that
	 * is hold in session scope as managed bean "uweContextFactory".
	 */
	public static AbstractUWEContextFactory getCurrentInstance() {
		return (AbstractUWEContextFactory) ExpressionHelpers
				.resolveValue("#{uweContextFactory}");
	}

	
	@Override
	public Map<String, Object> getManagedBeans() {
		Map<String, Object> result = new HashMap<String, Object>();
		List<String> beanNames = getManagedBeanNames();
		FacesContext context = FacesContext.getCurrentInstance();
		Application application = FacesContext.getCurrentInstance()
				.getApplication();

		for (String beanName : beanNames) {
			Object bean = application.evaluateExpressionGet(context, "#{"
					+ beanName + "}", Object.class);
			result.put(beanName, bean);
		}

		// Use session as an alias for OGNL expressions. Remember that
		// "session" is no valid bean name in JSF.
		if (result.get("uweSession") != null)
			result.put("session", result.get("uweSession"));
		return result;

	}

	@Override
	public PersistenceManager getPersistenceManager() {
		PersistenceManager pm = (PersistenceManager) ExpressionHelpers
				.resolveValue("#{uwePersistenceManager}");
		return pm;
	}

	@Override
	public ScriptContext createScriptContext(Object contextObject) {
		Map<String, Object> beans = getManagedBeans();

		RootBeanScriptContext context = new RootBeanScriptContext(contextObject);

		Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);

		// properties of the context object can be overwritten by equally named
		// managed beans
		bindings.putAll(beans);
		UWEScriptHelper scriptHelper = new UWEScriptHelper(this);
		PersistenceManager pm = getPersistenceManager();

		bindings.put("uweHelper", scriptHelper);
		bindings.put("uwePersistenceManager", pm);

		return context;
	}

}
